var mongo = require('mongodb').MongoClient;
var BSON = require('mongodb').BSONPure;

config = require('./config');

// helper function. Extends the string object to have an endsWith function
String.prototype.endsWith = function (suffix) {
    return this.indexOf(suffix, this.length - suffix.length) !== -1;
};

exports.addProdDetailsStatic = function (req, res) {

    mongo.connect(config.dbUri, function (err, db) {

        // if there is an error during connection, return an error
        if (err) {
            res.json({ error: err });
            return;
        }

        if (db) {

            // if there is no _id field in the object, Mongo will automatically create the _id
            // here we are always inserting the same object again and again with a new Id
            // TODO: read the json from the request body and insert it appropriately
            db.collection('products').insert({
                tags: ["Book", "Paperback", "Electronic"],
                title: "NoSQL distilled",
                desc: "",
                price: 24.99,
                isbn: "978-0-321-82662-612301-18",
                pages: 164,
                authors: ["Pramod Sadalage", "Martin Fowler"]
            }, function (err, inserted) {
                // when the insert is finished, this callback function is called
                if (err) {
                    res.json({ error: err });
                    return;
                }

                res.json(inserted);
                return;
            });
        }
    });
};

exports.addProdDetails = function (req, res) {

    if (!req.body) {
        res.json({ error: "empty body" });
        return;
    }

    mongo.connect(config.dbUri, function (err, db) {

        // if there is an error during connection, return an error
        if (err) {
            res.json({ error: err });
            return;
        }

        if (db) {

            // if there is no _id field in the object, Mongo will automatically create the _id
            // we read the body from the request and insert it in mongo
            db.collection('products').insert(req.body, function (err, inserted) {
                // when the insert is finished, this callback function is called
                if (err) {
                    res.json({ error: err });
                    return;
                }

                res.json(inserted);
                return;
            });
        }
    });
};




// Note: this function implements the following three endpoints
// CREATE, READ product
//GET    <product-uri>/api/product <--- this lists out all the products. Not asked for
//GET    <product-uri>/api/product/{prodId}
//GET    <product-uri>/api/product/{prodId}/details

exports.getProduct = function (req, res) {
    mongo.connect(config.dbUri, function (err, db) {
        if (err) {
            console.log("error");
            return;
        }

        if (db) {
            db.collection('products', function (err, collection) {

                // create an empty query parameter object for querying mongo
                var queryParams = {};
                var fieldLimiters = {};
                // if productId exists in the url, extract it and add it to mongo query
                if (req.params.productId) {
                    // You need to convert the string to BSON format object id for the search to succeed
                    var obj_id = BSON.ObjectID.createFromHexString(req.params.productId);
                    queryParams._id = obj_id;

                    // I am sure this is a hack and there is a better way to do this
                    // but I am just checking if the existing path does not ends with 'details'
                    // If it doesn't, I just filter down the results to include only a few fields
                    if (!req.route.path.endsWith("details")) {
                        
                        fieldLimiters._Id = true;
                        fieldLimiters.tags = true;
                        //fieldLimiters.title = true;
                        fieldLimiters.prodName = true;
                        fieldLimiters.prodDesc = true;
                        fieldLimiters.prodPrice = true;
                        fieldLimiters.imageUrl = true;
                    }
                }
               
                // we pass the query parameters to the function. If you invoke without a query parameter,
                // it will return everything
                collection.find(queryParams, fieldLimiters).toArray(function (err, items) {
                    // toArray function iterates over the entire cursor and creates an array out of it
                    // the array is available in the 'items' parameter. This is somewhat inefficient
                    // and can potentially consume a ton of memory
                    // TODO: research the 'stream' way of writing data.
                    if (err) {
                        res.json({ error: err });
                    }
                    
                    items = transformprodID(items);

                    var retObj = { totalCount: items.length, products: items }
                    console.log(retObj);

                    // return the entire array as a JSON blob
                    // TODO: convert the names of the elements from input schema to output schema
                    
                    res.json(retObj);
                    return;
                });
            });
        }
    });
};


// implements the following:
// SEARCH products by product Id, tag, or name
// GET    <product-uri>/api/product/search/prodid?key=1001&key=1002
// GET    <product-uri>/api/product/search/tag?key=book&pageIndex={index}&pageSize={size}
// GET    <product-uri>/api/product/search/name?key=sql&pageIndex={index}&pageSize={size}
// TODO: paging and page size yet to be implemented
exports.search = function (req, res) {
    mongo.connect(config.dbUri, function (err, db) {
        if (err) {
            console.log("error");
            return;
        }

        // if the same query parameter repeats multiple time, express puts them all in an array
        // get the array here
        var keysArray = req.query["key"];

        // extracts the 'key' query parameters out in form of an array of object IDs
        function getKeys() {
            // SuperHacky - the above returns an array if we have more than one key params
            // but if only one is there, then we get a string back. If we get just one string
            // I am forcing it to be an array so that we can have the rest of code assuming an array
            if (typeof (keysArray) === 'string') {
                console.log("entering single key blob");
                keysArray = [req.query["key"]];
            }

            console.log(keysArray);
            // for productIDs, convert them to the BSON objectIDs else since that is how IDs are stored
            for (var i = 0; i < keysArray.length; i++) {
                console.log(keysArray[i]);
                keysArray[i] = BSON.ObjectID.createFromHexString(keysArray[i]);
            }

            return keysArray;
        }

        if (db) {
            db.collection('products', function (err, collection) {

                var queryParams = {};

                if (req.route.path.endsWith("prodid")) {
                    //console.log("ends with prodid" + req.query["key"]);
                    keysArray = getKeys();
                    // setup the query for search - the following says that the _id field can be exact match
                    // for any of the elements in the array. See http://docs.mongodb.org/manual/reference/operator/in/
                    queryParams = { _id: { $in: keysArray } };
                }
                else if (req.route.path.endsWith("tag")) {
                    console.log("ends with tag");

                    // the $in matches inside arrays as well. See http://docs.mongodb.org/manual/reference/operator/in/
                    queryParams = { tags: { $in: [req.query["key"]] } };
                }
                else if (req.route.path.endsWith("name")) {
                    console.log(req.query["key"]);

                    // substring match for the title field. Says, first part match is substring, 'i' means ignore case.
                    queryParams = { title: { $regex: req.query["key"], $options: 'i' } };
                }
                else {
                    req.json({ message: "not found" });
                    return;
                }

                if (req.route.path.endsWith("prodid")) {
                    collection.find(queryParams).toArray(function (err, items) {
                        // toArray function iterates over the entire cursor and creates an array out of it
                        // the array is available in the 'items' parameter. This is somewhat inefficient
                        // and can potentially consume a ton of memory
                        // TODO: research the 'stream' way of writing data.
                        if (err) {
                            res.json({ error: err });
                        }

                        console.log(items);

                        // return the entire array as a JSON blob
                        // TODO: convert the names of the elements from input schema to output schema
                        items = transformprodID(items);
                        var retObj = { totalCount: items.length, products: items }
                        res.json(retObj);
                        return;
                    });
                }
                else if (req.route.path.endsWith("name")
                    || req.route.path.endsWith("tag")) {

                    var pageIndex = parseInt(req.query["pageIndex"]);
                    var pageSize = parseInt(req.query["pageSize"]);

                    if (isNaN(pageIndex) || isNaN(pageSize)) {
                        res.json({ error: "pageIndex or pageSize improperly specified" });
                        return;
                    }

                    collection.find(queryParams)
                        .skip(pageIndex * pageSize) // the skip allows for pagination because we skip pageIndex*pageSize
                        .limit(pageSize) // pagesize limits how much to return in the result page
                        .toArray(function (err, items) {
                            // toArray function iterates over the entire cursor and creates an array out of it
                            // the array is available in the 'items' parameter. This is somewhat inefficient
                            // and can potentially consume a ton of memory
                            // TODO: research the 'stream' way of writing data.
                            if (err) {
                                res.json({ error: err });
                            }
                            // return the entire array as a JSON blob
                            // TODO: convert the names of the elements from input schema to output schema
                            items = transformprodID(items);

                            var retObj = { totalCount: items.length, products: items }
                            res.json(retObj);
                            return;
                        });
                }
            });

        }
    });
}


// Implement deleting a single product using the following REST endpoint
// DELETE <product-uri>/api/product/:productId
exports.deleteProduct = function (req, res) {
    mongo.connect(config.dbUri, function (err, db) {
        if (err) {
            console.log("error");
            return;
        }

        console.log(req.params.productId);
        var queryParams = {};
        queryParams._Id = BSON.ObjectID.createFromHexString(req.params.productId);
        if (db) {
            db.collection('products').remove(queryParams, function (err, item) {
                if (err) {
                    console.log(err);
                    res.json({ error: "error happend" + err });
                    return;
                }
                res.json(item);
                return;
            });
        };
    });
};

exports.getAllTags = function (req, res) {
    mongo.connect(config.dbUri, function (err, db) {
        if (err) {
            console.log("error");
            return;
        }

        if (!db) {
            res.json({ error: "no db connection" });
        }

        db.collection('products', function (err, collection) {
            collection.distinct('tags', function (err, result) {
                res.json(result);
                return;
            });
        });

    });
};

function transformprodID(items) {
    for (var i = 0; i < items.length; i++) {
        items[i].prodId = items[i]._id;
    }
    return items;
}